﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using OpenTK.Graphics.OpenGL;
using System.Drawing;
using System.Drawing.Imaging;

namespace LDLib
{
    /// <summary>
    /// Helper class for loading embedded resources.
    /// You can call these loading function anytime, and you don't
    /// have to worry about re-loading a resource you've previously
    /// loaded - the previously loaded resource will be retrieved
    /// instead. :)
    /// </summary>
    public static class AssetLoader
    {
        /// <summary>
        /// Loads an internal shader resource
        /// </summary>
        /// <param name="name">Name to call the shader</param>
        /// <param name="vertexSource">Name of the vertex resource</param>
        /// <param name="fragmentSource">Name of the fragment resource</param>
        /// <returns></returns>
        internal static Shader LoadShaderInternal(string name, string vertexSource, string fragmentSource)
        {
            if (R.Shaders.ContainsKey(name)) return R.Shaders[name];

            string vText = null, fText = null;
            string[] intRes = Assembly.GetExecutingAssembly().GetManifestResourceNames();
            foreach (string s in intRes)
            {
                if (vText == null && s.EndsWith(vertexSource)) vText = StreamToString(Assembly.GetExecutingAssembly().GetManifestResourceStream(s));
                if (fText == null && s.EndsWith(fragmentSource)) fText = StreamToString(Assembly.GetExecutingAssembly().GetManifestResourceStream(s));
                if (vText != null && fText != null) break;
            }

            if (vText != null && fText != null)
            {
                Shader shader = new Shader(vText, fText);
                R.Shaders.Add(name, shader);
                return shader;
            }

            throw new Exception("Shader '" + (vText == null ? vertexSource : fragmentSource) + "' not found (did you forget to set the build action to 'Embedded Resource'?)");
        }

        /// <summary>
        /// Loads a shader from the embedded resources and stores it to the
        /// resources.
        /// </summary>
        /// <param name="name">Name you wish to use to call the shader</param>
        /// <param name="vertexSource">Name of the vertex resource, i.e. "DefaultVertexShader.glsl"</param>
        /// <param name="fragmentSource">Name of the fragment resource, i.e. "DefaultFragmentShader.glsl"</param>
        /// <returns></returns>
        public static Shader LoadShader(string name, string vertexSource, string fragmentSource)
        {
            if (R.Shaders.ContainsKey(name)) return R.Shaders[name];

            string vText = null, fText = null;
            string[] extRes = Assembly.GetEntryAssembly().GetManifestResourceNames();
            foreach (string s in extRes)
            {
                if (vText == null && s.EndsWith(vertexSource)) vText = StreamToString(Assembly.GetEntryAssembly().GetManifestResourceStream(s));
                if (fText == null && s.EndsWith(fragmentSource)) fText = StreamToString(Assembly.GetEntryAssembly().GetManifestResourceStream(s));
                if (vText != null && fText != null) break;
            }
            
            if(vText != null && fText != null)
            {
                Shader shader = new Shader(vText, fText);
                R.Shaders.Add(name, shader);
                return shader;
            }

            throw new Exception("Shader '" + (vText == null ? vertexSource : fragmentSource) + "' not found (did you forget to set the build action to 'Embedded Resource'?)");
        }

        /// <summary>
        /// Loads a texture from the embedded resources and
        /// stores it in the resources.
        /// </summary>
        /// <param name="name">Name of the texture</param>
        /// <param name="textureSource">Name of the source</param>
        /// <returns>ID of the texture or -1 if not found</returns>
        public static int LoadTexture(string name, string textureSource, TextureMinFilter minFilter = TextureMinFilter.Linear, TextureMagFilter magFilter = TextureMagFilter.Linear, TextureWrapMode wrapS = TextureWrapMode.ClampToEdge, TextureWrapMode wrapT = TextureWrapMode.ClampToEdge)
        {
            if (R.Textures.ContainsKey(name)) return R.Textures[name];

            string[] extRes = Assembly.GetEntryAssembly().GetManifestResourceNames();
            foreach(string s in extRes)
            {
                if(s.EndsWith(textureSource))
                {
                    Bitmap bitmap = new Bitmap(Assembly.GetEntryAssembly().GetManifestResourceStream(s));
                    BitmapData bd = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

                    int id = GL.GenTexture();
                    GL.BindTexture(TextureTarget.Texture2D, id);
                    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)minFilter);
                    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)magFilter);
                    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)wrapS);
                    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)wrapT);
                    GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bitmap.Width, bitmap.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bd.Scan0);
                    GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
                    bitmap.UnlockBits(bd);
                    bitmap.Dispose();
                    R.Textures.Add(name, id);
                    return id;
                }
            }

            throw new Exception("Texture '" + textureSource + "' not found (did you forget to set the build action to 'Embedded Resource'?)");
        }

        /// <summary>
        /// Loads a bitmap font from the embedded resources and
        /// stores it in the resources.
        /// </summary>
        /// <param name="name">Name of the font</param>
        /// <param name="fontSource">Name of the source</param>
        /// <returns>The loaded bitmap font</returns>
        public static BitmapFont LoadFont(string name, string fontSource)
        {
            if (R.Fonts.ContainsKey(name)) return R.Fonts[name];

            string[] extRes = Assembly.GetEntryAssembly().GetManifestResourceNames();
            foreach(string s in extRes)
            {
                if(s.EndsWith(fontSource))
                {
                    string fnt = StreamToString(Assembly.GetEntryAssembly().GetManifestResourceStream(s));
                    BitmapFont font = new BitmapFont(fnt.Split('\n'));
                    return font;
                }
            }

            throw new Exception("Font '" + fontSource + "' not found (did you forget to set the build action to 'Embedded Resource'?)");
        }

        /// <summary>
        /// Reads a string from a stream
        /// </summary>
        /// <param name="s">Stream to read from</param>
        /// <returns>String contained in the stream</returns>
        private static string StreamToString(Stream s)
        {
            if (s == null) return "";

            string result = "";
            StreamReader reader = new StreamReader(s);
            result = reader.ReadToEnd();
            return result;
        }
    }
}
